//////////////////////////////////////////////////
// glRenderer.cpp
//
// Author: Tarik Belabbas et Adrien Hoffer
//////////////////////////////////////////////////

#include "glRenderer.h"
#include "glShaderProgram.h"
#include "../common/Common.h"
#include "particles/glParticleManager.h"

ST_SINGLETON_INIT( glRenderer );

//------------------------------------------------------------------------------
glRenderer::glRenderer()
{
	m_shaderProgram	= new glShaderProgram();
	m_camera		= new glCamera();
}

//------------------------------------------------------------------------------
glRenderer::~glRenderer()
{
	delete( m_shaderProgram );
	delete( m_camera );
}

//------------------------------------------------------------------------------
void glRenderer::initialize()
{
	glEnable( GL_TEXTURE_2D );		// Enable Texture Mapping
	glShadeModel( GL_SMOOTH );		// Enable Smooth Shading

	//glClearColor( 23.0f / 255.0f, 23.0f / 255.0f, 73.0f / 255.0f, 0.0f );
	glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
	glClearDepth( 1.0f );			// Depth Buffer Setup

	glEnable( GL_DEPTH_TEST );		// Enables Depth Testing
	glDepthFunc( GL_LEQUAL );		// The Type Of Depth Testing To Do

	// Really Nice Perspective Calculations
	glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

	// Transparency
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	if ( !m_shaderProgram->initialize( "data/shaders/default.vs",
			"data/shaders/default.fs"/*,
			"data/shaders/geometry_shader.glsl"*/ ) )
	{
		ST_ERROR( ( "Could not initialize the shaders\n" ) );
	}

	// Bind the attribute locations
	m_shaderProgram->bindAttribute( 0, "a_Vertex" );
	m_shaderProgram->bindAttribute( 1, "a_Color" );
	m_shaderProgram->bindAttribute( 2, "a_TexCoord" );

	// Re-link the program
	m_shaderProgram->linkProgram();

	// Initialize axis and other models
	m_attributes.setInterlacing( true );
	initializeAxis();

	m_vertexBO.genBuffer();
	m_vertexBO.storeData( GL_ARRAY_BUFFER, GL_STATIC_DRAW,
			m_attributes.getRawData(), m_attributes.getRawSize() );

	/*m_attributesCube.setInterlacing( true );
	initializeCube();

	m_vertexBOCube.genBuffer();
	m_vertexBOCube.storeData( GL_ARRAY_BUFFER, GL_STATIC_DRAW,
			m_attributesCube.getRawData(), m_attributesCube.getRawSize() );*/

	// Create light components
	/*GLfloat ambientLight[]	= { 0.2f, 0.2f, 0.2f, 1.0f };
	GLfloat diffuseLight[]	= { 0.8f, 0.8f, 0.8, 1.0f };
	GLfloat specularLight[]	= { 0.5f, 0.5f, 0.5f, 1.0f };
	GLfloat position[]		= { 0.0f, 0.0f, 10000.0f, 1.0f };

	// Assign created components to GL_LIGHT0
	glLightfv( GL_LIGHT0, GL_AMBIENT, ambientLight );
	glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuseLight );
	glLightfv( GL_LIGHT0, GL_SPECULAR, specularLight );
	glLightfv( GL_LIGHT0, GL_POSITION, position );

	glEnable( GL_LIGHTING );
	glEnable( GL_LIGHT0 );*/
}

//------------------------------------------------------------------------------
void glRenderer::initializeAxis()
{
	// X axis
	m_attributes.add3( 0.0f, 0.0f, 0.0f );
	m_attributes.add4( 1.0f, 1.0f, 1.0f, 1.0f );
	m_attributes.add3( 1.0f, 0.0f, 0.0f );
	m_attributes.add4( 1.0f, 1.0f, 1.0f, 1.0f );

	// Y axis
	m_attributes.add3( 0.0f, 0.0f, 0.0f );
	m_attributes.add4( 1.0f, 1.0f, 1.0f, 1.0f );
	m_attributes.add3( 0.0f, 1.0f, 0.0f );
	m_attributes.add4( 1.0f, 1.0f, 1.0f, 1.0f );

	// Z axis
	m_attributes.add3( 0.0f, 0.0f, 0.0f );
	m_attributes.add4( 1.0f, 0.0f, 0.0f, 1.0f );
	m_attributes.add3( 0.0f, 0.0f, 1.0F );
	m_attributes.add4( 1.0f, 0.0f, 0.0f, 1.0f );
}

//------------------------------------------------------------------------------
void glRenderer::render()
{
	float modelviewMatrix[ 16 ];
	float projectionMatrix[ 16 ];

	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	// Load the identity matrix (reset to the default position and orientation)
	glLoadIdentity();

	m_camera->update();
	m_camera->updateMove();
	glScalef( 1.0f, 1.0f, 1.0f );

	// Get the current matrices from OpenGL
	glGetFloatv( GL_MODELVIEW_MATRIX, modelviewMatrix );
	glGetFloatv( GL_PROJECTION_MATRIX, projectionMatrix );

	// Send the modelview and projection matrices to the shaders
	m_shaderProgram->sendUniform( "modelview_matrix", modelviewMatrix );
	m_shaderProgram->sendUniform( "projection_matrix", projectionMatrix );
	//m_shaderProgram->sendUniform( "texture0", NULL );

	glParticleManager::s_getInstance()->render();

	//m_shaderProgram->useProgram();
	//renderAxis();
	//m_shaderProgram->unuseProgram();

	/*QPainter painter;
	painter.begin( this );*/

	/*QString framesPerSecond;
	framesPerSecond.setNum( m_frames / ( m_time.elapsed() / 1000.0 ), 'f', 2 );*/

	/*painter.setPen( Qt::yellow );

	painter.drawText( 20, 40, framesPerSecond + " FPS" );

	painter.end();*/

	/*if ( !( m_frames % 100 ) )
	{
		m_time.start();
		m_frames = 0;
	}

	m_frames++;*/
}

//------------------------------------------------------------------------------
void glRenderer::renderAxis()
{
	glEnableVertexAttribArray( 0 ); // Enable the vertex attribute
	glEnableVertexAttribArray( 1 ); // Enable the color attribute

	// Bind the vertex array
	m_vertexBO.bindBuffer( GL_ARRAY_BUFFER );
	glVertexAttribPointer( ( GLint ) 0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof( float ),
			BUFFER_OFFSET( 0 ) );

	// Bind the color array
	glVertexAttribPointer( ( GLint ) 1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof( float ),
			BUFFER_OFFSET( 3 * sizeof( float ) ) );

	// Draw the elements
	glDrawArrays( GL_LINES, 0, 2 );
	glDrawArrays( GL_LINES, 2, 2 );
	glDrawArrays( GL_LINES, 4, 2 );

	glDisableVertexAttribArray( 1 ); // Enable the color attribute
	glDisableVertexAttribArray( 0 ); // Enable the vertex attribute
}

//------------------------------------------------------------------------------
void glRenderer::resizeViewport( int _width, int _height )
{
	if ( _height == 0 )
	{
		_height = 1;
	}

	glViewport( 0, 0, ( GLsizei ) _width, ( GLsizei ) _height );

	m_camera->resizeViewport( ( float )_width / ( float )_height );
}

//------------------------------------------------------------------------------
void glRenderer::moveCameraTo( float _posX, float _posY, float _posZ )
{
	m_camera->moveCameraTo( _posX, _posY, _posZ );
}

//------------------------------------------------------------------------------
void glRenderer::addFire( float _posX, float _posY, float _posZ )
{
	unsigned int emitter = glParticleManager::s_getInstance()->addEmitter();
	glParticleManager::s_getInstance()->addTexture( emitter, "data/textures/fire0.png" );
	glParticleManager::s_getInstance()->addTexture( emitter, "data/textures/fire1.png" );
	glParticleManager::s_getInstance()->addTexture( emitter, "data/textures/fire2.png" );
	glParticleManager::s_getInstance()->addTexture( emitter, "data/textures/fire3.png" );
	glParticleManager::s_getInstance()->addTexture( emitter, "data/textures/fire4.png" );
	glParticleManager::s_getInstance()->addTexture( emitter, "data/textures/smoke.png" );
	glParticleManager::s_getInstance()->setPosition( emitter, glm::vec3( _posX, _posY, _posZ ) );
	glParticleManager::s_getInstance()->setDirection( emitter, glm::vec3( 0.0f, 1.0f, 0.0f ) );
	glParticleManager::s_getInstance()->setNbParticles( emitter, 100000 );

	glParticleManager::s_getInstance()->initialize( emitter );
}
